using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;

namespace Zune.Forms
{
    /// <summary>
    /// A ListBox control
    /// </summary>
    public class ListBox : Control
    {
        #region Public Events
        public event EventHandler ItemClicked;
        #endregion

        #region Private Members
        private enum ScrollDirections
        {
            Up,
            Down
        }
        private List<ListItem> _items = new List<ListItem>();
        private string _fontName;
        private SpriteFont _font;
        private Vector2 _buttonStartPos = new Vector2(0, 0);
        private Vector2 _startPos = new Vector2(0, 0);
        private Vector2 _currPos = new Vector2(0, 0);
        private Vector2 _endPos = new Vector2(0, 0);
        private float _y = 0;
        private float _prevY = 0;
        private float _fHeight = 0;
        private float _itemYOffset = 10;
        private bool _allowSelection = true;
        private List<ListItem> _selectedItems = new List<ListItem>();
        private Texture2D _vLine;
        private Texture2D _hLine;
        private bool _displayBorder = true;
        private int _borderWidth = 2;
        private bool _isTouching = false;
        private System.Threading.Timer _clickTimer;
        private bool _readyToClick = false;
        private System.Threading.Timer _releaseTimer;
        private Color _liForeColor = Color.White;
        private Color _liAltForeColor = Color.White;
        private Color _liAltRowBackColor = Color.Black;
        private Color _liHighlightForeColor = Color.Black;
        private Color _liAltHighlightForeColor = Color.Black;
        private Color _liSelectRowColor = Color.White;
        private Color _liAltSelectRowColor = Color.White;
        private int _postTouchItemScrollTicks = 5;
        private int _postTouchCurrItemScrollCount = 0;
        private int _postTouchScrollSpeed = 1;
        private bool _shouldContinueScroll = false;
        private ScrollDirections _currentScrollDirection = ScrollDirections.Up;
        #endregion

        #region Constructors
        /// <summary>
        /// A ListBox.
        /// </summary>
        /// <param name="form"></param>
        public ListBox(Form form) : base(form)
        {
            base.BackColor = Color.Black;
            base.Size = new Vector2(272, 480);
            _clickTimer = new System.Threading.Timer(ClickTimer_Tick, null, System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite);
            _releaseTimer = new System.Threading.Timer(ReleaseTimer_Tick, null, System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite);
        }

        public ListBox(Control control)
            : base(control)
        {
            base.BackColor = Color.Black;
            base.Size = new Vector2(272, 480);
            _clickTimer = new System.Threading.Timer(ClickTimer_Tick, null, System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite);
            _releaseTimer = new System.Threading.Timer(ReleaseTimer_Tick, null, System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite);
        }
        #endregion

        #region Overrides
        public override void LoadContent()
        {
            _startPos = base.Location;
            _currPos = base.Location;
            if (!string.IsNullOrEmpty(_fontName))
            {
                _font = base.Form.Content.Load<SpriteFont>(_fontName);
            }
            else
            {
                _font = base.Form.Content.Load<SpriteFont>("DefaultFont");
            }
            _fHeight = _font.MeasureString("TEST").Y;
            base.LoadContent();
        }

        public override void Draw(GameTime gameTime)
        {
            if (_visible)
            {
                //Did we go too far before the first item?
                if (_y < 10 && !_isTouching)
                {
                    if (_y < 15)
                    {
                        _y = 0;
                    }
                    else if (_y <= 30)
                    {
                        _y -= 15;
                    }
                    else if (_y > 30 && _y <= 100)
                    {
                        _y -= 20;
                    }
                    else if (_y > 100)
                    {
                        _y -= 40;
                    }
                    else
                    {
                        _y = 0;
                    }
                    _currPos = base.Location;
                    _startPos = base.Location;
                    _prevY = _y;
                }
                //Did we go past the last item?
                else if (_y < _size.Y - (_items.Count * _fHeight) && !_isTouching)
                {
                    float _tempY = _size.Y - (_items.Count * _fHeight) - _y;
                    if (_tempY < 15)
                    {
                        _y = _size.Y - (_items.Count * _fHeight) - 20;
                    }
                    if (_tempY <= 50)
                    {
                        _y += 15;
                    }
                    else if (_tempY > 50 && _tempY <= 110)
                    {
                        _y += 20;
                    }
                    else if (_tempY > 110)
                    {
                        _y += 40;
                    }
                    else
                    {
                        _y = _size.Y - (_items.Count * _fHeight) - 20;
                    }

                    _currPos = new Vector2(_currPos.X, _y);
                    _startPos = _currPos;
                    _prevY = _y;
                }
                else if (_isTouching)
                {
                    _y = _currPos.Y - _startPos.Y + _prevY;
                }
                else if (_shouldContinueScroll)
                {
                    if (_y > 10 | _y < _size.Y - (_items.Count * _fHeight))
                    {
                        _shouldContinueScroll = false;
                        _postTouchCurrItemScrollCount = 0;
                        _y = _currPos.Y - _startPos.Y + _prevY;
                    }
                    else
                    {
                        switch (_currentScrollDirection)
                        {
                            case ScrollDirections.Up:
                                _y += _fHeight / 2 * _postTouchScrollSpeed;
                                break;
                            case ScrollDirections.Down:
                                _y -= _fHeight / 2 * _postTouchScrollSpeed;
                                break;
                        }
                        _currPos = new Vector2(_currPos.X, _y);
                        _startPos = _currPos;
                        _prevY = _y;
                        _postTouchCurrItemScrollCount++;
                        if (_postTouchItemScrollTicks >= _postTouchCurrItemScrollCount)
                        {
                            _shouldContinueScroll = false;
                            _postTouchCurrItemScrollCount = 0;
                        }
                    }
                }

                Vector2 currentPos = new Vector2(base.Location.X + 10, base.Location.Y + _y);
                Texture2D background = Drawing.CreateRectangle(base.SpriteBatch.GraphicsDevice, (Int32)base.Size.X, (Int32)base.Size.Y, _backColor);
                base.SpriteBatch.Draw(background, base.Location, Color.White);

                foreach (ListItem item in _items)
                {
                    item._parentBorderWidth = _borderWidth;
                    item._formColor = base.Form.BackColor;
                    item._parentSize = base.Size;
                    item._parentLocation = base.Location;
                    item._itemHeight = _fHeight;
                    item._location = currentPos;
                    item._size = new Vector2(base.Size.X - 10, _fHeight);
                    item.Draw(base.SpriteBatch, _font);
                    currentPos = new Vector2(currentPos.X, currentPos.Y + _fHeight + _itemYOffset);
                }

                if (_displayBorder)
                {
                    _hLine = Drawing.CreateLine(base.GraphicsDevice, 0, 0, (Int32)base.Size.X, _borderWidth, Color.White);
                    _vLine = Drawing.CreateLine(base.GraphicsDevice, 0, 0, _borderWidth, (Int32)base.Size.Y, Color.White);

                    base.SpriteBatch.Draw(_hLine, new Vector2(base.Location.X, base.Location.Y), Color.White);
                    base.SpriteBatch.Draw(_hLine, new Vector2(base.Location.X, base.Location.Y + base.Size.Y - _borderWidth), Color.White);
                    base.SpriteBatch.Draw(_vLine, new Vector2(base.Location.X, base.Location.Y), Color.White);
                    base.SpriteBatch.Draw(_vLine, new Vector2(base.Location.X + base.Size.X - _borderWidth, base.Location.Y), Color.White);
                }

                base.Draw(gameTime);
            }
        }

        public override void Update(GameTime gameTime, TouchCollection touches)
        {
            if (touches.Count > 0)
            {
                
                TouchLocation touch = touches[0];

                if (Utilities.IsControlTouched(touch.Position, base.Location, base.Size))
                {
                    _isTouching = true;

                    switch (touch.State)
                    {
                        case TouchLocationState.Pressed:
                            _startPos = touch.Position;
                            _currPos = touch.Position;
                            _endPos = touch.Position;
                            _buttonStartPos = touch.Position;
                            _clickTimer.Change(20, System.Threading.Timeout.Infinite);
                            break;
                        case TouchLocationState.Moved:
                            if ((touch.Position.X - _buttonStartPos.X < 5 || touch.Position.X + _buttonStartPos.X < 5) && (touch.Position.Y - _buttonStartPos.Y < 5 || touch.Position.Y + _buttonStartPos.Y < 5) && _allowSelection)
                            {
                                if (_readyToClick)
                                {
                                    foreach (ListItem item in _items)
                                        item.Update(touch);
                                }
                            }
                            else
                            {
                                _clickTimer.Change(System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite);
                                _releaseTimer.Change(10, System.Threading.Timeout.Infinite);
                                _readyToClick = false;
                            }

                            _currPos = touch.Position;
                            break;
                        case TouchLocationState.Released:
                            if (gameTime.ElapsedGameTime.Milliseconds < 250)// && (touch.Position.X - _buttonStartPos.X < 5 || touch.Position.X + _buttonStartPos.X < 5) && (touch.Position.Y - _buttonStartPos.Y < 5 || touch.Position.Y + _buttonStartPos.Y < 5) && _allowSelection)
                            {
                                foreach (ListItem item in _items)
                                    item.Update(touch);
                            }
                            if (_startPos.Y > touch.Position.Y)
                            {
                                _currentScrollDirection = ScrollDirections.Down;
                                if (_startPos.Y - touch.Position.Y > 10)
                                {
                                    _shouldContinueScroll = true;
                                    DeterminePostScrollSpeed(_startPos, touch.Position);
                                }
                            }
                            else if (_startPos.Y < touch.Position.Y)
                            {
                                _currentScrollDirection = ScrollDirections.Up;
                                if (touch.Position.Y - _startPos.Y  > 10)
                                {
                                    _shouldContinueScroll = true;
                                    DeterminePostScrollSpeed(_startPos, touch.Position);
                                }
                            }
                            _releaseTimer.Change(250, System.Threading.Timeout.Infinite);
                            _startPos = base.Location;
                            _endPos = touch.Position;
                            _currPos = base.Location;
                            _prevY = _y;
                            _isTouching = false;
                            _buttonStartPos = base.Location;
                            _readyToClick = false;
                            break;
                    }
                }
                else
                {
                    _isTouching = false;
                }
                base.Update(gameTime, touches);
            }
        }

        private void DeterminePostScrollSpeed(Vector2 start, Vector2 end)
        {
            float diff = 0;
            switch (_currentScrollDirection)
            {
                case ScrollDirections.Down:
                    diff = start.Y - end.Y;
                    break;
                case ScrollDirections.Up:
                    diff = end.Y - start.Y;
                    break;
            }
            if (diff >= 10 && diff <= 20)
            {
                _postTouchScrollSpeed = 2;
                _postTouchItemScrollTicks = 20;
            }
            else if (diff > 20 && diff <= 30)
            {
                _postTouchScrollSpeed = 4;
                _postTouchItemScrollTicks = 30;
            }
            else if (diff > 30 && diff <= 50)
            {
                _postTouchScrollSpeed = 6;
                _postTouchItemScrollTicks = 40;
            }
            else if (diff > 50)
            {
                _postTouchScrollSpeed = 8;
                _postTouchItemScrollTicks = 50;
            }
        }

        public void AddItem(ListItem item)
        {
            item._altForeColor = _liAltForeColor;
            item._altHighlightForeColor = _liAltHighlightForeColor;
            item._altRowBackColor = _liAltRowBackColor;
            item._altSelectRowColor = _liAltSelectRowColor;
            item._foreColor = _liForeColor;
            item._highlightForeColor = _liHighlightForeColor;
            item._selectRowColor = _liSelectRowColor;
            item.ItemClick += new EventHandler(item_ItemClick);
            _items.Add(item);
            item._index = _items.IndexOf(item);
        }
        #endregion

        #region Internal Event Handlers
        internal void ClickTimer_Tick(object obj)
        {
            _readyToClick = true;
            _clickTimer.Change(System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite);
        }

        internal void ReleaseTimer_Tick(object obj)
        {
            _releaseTimer.Change(System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite);
            foreach (ListItem item in _items)
            {
                item._isPressed = false;
            }
        }

        internal void item_ItemClick(object sender, EventArgs e)
        {
            _selectedItems.Clear();
            _selectedItems.Add((ListItem)sender);

            if (ItemClicked != null)
                ItemClicked(sender, e);
        }
        #endregion

        #region Public Properties
        /// <summary>
        /// Gets or sets the SpriteFont to use.
        /// </summary>
        public string Font
        {
            get { return _fontName; }
            set { _fontName = value; }
        }

        /// <summary>
        /// Gets or sets the width used for the Border.
        /// </summary>
        public int BorderWidth
        {
            get { return _borderWidth; }
            set { _borderWidth = value; }
        }

        /// <summary>
        /// Gets or sets the Items.
        /// </summary>
        public List<ListItem> Items
        {
            get { return _items; }
            set { _items = value; }
        }
        
        /// <summary>
        /// Gets or sets whether items in the ListBox can be selected.
        /// </summary>
        public bool AllowItemSelection
        {
            get { return _allowSelection; }
            set { _allowSelection = false; }
        }

        /// <summary>
        /// Gets or sets whether the border is displayed.
        /// </summary>
        public bool DisplayBorder
        {
            get { return _displayBorder; }
            set { _displayBorder = value; }
        }

        public Color ListItemForeColor
        {
            get { return _liForeColor; }
            set { _liForeColor = value; }
        }

        public Color AlternateRowListItemForeColor
        {
            get { return _liAltForeColor; }
            set { _liAltForeColor = value; }
        }

        public Color AlternateRowListItemRowBackColor
        {
            get { return _liAltRowBackColor; }
            set { _liAltRowBackColor = value; }
        }

        public Color ListItemHighlightForeColor
        {
            get { return _liHighlightForeColor; }
            set { _liHighlightForeColor = value; }
        }

        public Color AlternateRowListItemHighlightForeColor
        {
            get { return _liAltHighlightForeColor; }
            set { _liAltHighlightForeColor = value; }
        }

        public Color ListItemSelectRowColor
        {
            get { return _liSelectRowColor; }
            set { _liSelectRowColor = value; }
        }

        public Color AlternateRowListItemSelectRowColor
        {
            get { return _liAltSelectRowColor; }
            set { _liAltSelectRowColor = value; }
        }

        public float ItemYOffset
        {
            get { return _itemYOffset; }
            set { _itemYOffset = value; }
        }

        public void RemoveItemAt(int index)
        {
            _items.RemoveAt(index);
        }

        public void InsertItemAt(int index, ListItem listItem)
        {
            _items.Insert(index, listItem);
        }
        #endregion
    }
}
